#include "cplayer.h"
#include <cstdlib>
#include <iostream>

namespace ducks
{

CPlayer::CPlayer()
{
    nextDuck = 0;
    ducksKilled = 0;
    speciesModels = new HMM*[6];
    for(int i = 0; i < 6; ++ i)
        speciesModels[i] = NULL;
    blackProb = 0.7;
    blackProbGuess = 0.45;
    speciesIdentified = 0;
}

CAction CPlayer::Shoot(const CState &pState,const CDeadline &pDue)
{
    int numDucks = pState.GetNumDucks();
    int anlz = 0;
    int duckKill = -1;
    int bestObs;
    float probGuess = -1;
    bool isColoredCurrent = false;

    //Parameters have't been initialized
    if(duckModels == NULL)
    {
        this->duckModels = new HMM*[numDucks];

        for(int i = 0; i < numDucks; ++ i)
            this->duckModels[i] = new HMM(3, 9);

        obs = new vector<int>[numDucks];
    }

    while(anlz != numDucks)
    {
        if(pDue.RemainingUntil() < 100)
        {
            break;
        }
//        cout << "Analizing duck: " << this->nextDuck << endl;
        CDuck act = pState.GetDuck(this->nextDuck);
        if(act.IsDead())
        {
            this->nextDuck = (this->nextDuck + 1) % numDucks;
            ++ anlz;
            continue;
        }

        //Add the latest observations of the duck
        for(int j = obs[this->nextDuck].size(); j < act.GetSeqLength(); ++ j)
            obs[this->nextDuck].push_back(mapObsToInt2(act.GetAction(j)));

        if(obs[this->nextDuck].size() > 150)
        {
            HMM *actModel = duckModels[this->nextDuck];
            actModel->estimate(obs[this->nextDuck], 300);
            //We have a model for each species, check if this is not a black
            //bird
/*            if(this->speciesIdentified == 5)
            {
                float maxProb = 0.0;
                for(int i = 0; i < 6; ++ i)
                {
                    if(speciesModels[i] == NULL)
                        continue;
                    float modProb = speciesModels[i]->
                                        probabilityOf(obs[this->nextDuck]);

                    if(modProb > maxProb)
                        maxProb = modProb;
                }

                if(maxProb < blackProb)
                {
                    this->nextDuck = (this->nextDuck + 1) % numDucks;
                    ++ anlz;
                    continue;
                }
            }*/

            //Check if this duck matches with the model of any other duck. In
            //that case I can be sure it is not the black duck
            bool foundModel = false;
            bool isColored = false;
            for(int i = 0; i < numDucks; ++ i)
            {
                if(pDue.RemainingUntil() < 100)
                {
                    foundModel = false;
                    break;
                }
                //Do not try to make a match with the same duck or if the model
                //hasn't been updated with any observation of the original bird
                if((i == this->nextDuck) || (duckModels[i]->getCurrT() == 0))
                    continue;

                if(duckModels[i]->probabilityOf(this->obs[this->nextDuck]) > blackProb)
                {
                    //Try to determine if this is a colored duck
                    for(int i = 2; i < 6; ++ i)
                        if((this->speciesModels[i] != NULL) && 
                           (this->speciesModels[i]->
                                probabilityOf(this->obs[this->nextDuck]) > blackProb))
                        {
                            isColored = true;
                        }
                    foundModel = true;
                    break;
                }
            }

            //If a model matched this duck, consider it, since we can be sure
            //it's not endangered
            if(foundModel || (numDucks == 1))
            {
                int actGuess = actModel->calculateNextObservation();

                //If the model doesn't have any good prediction, reinitialize it
                if(actModel->getNextGuessProb() == 0)
                {
                    delete actModel;
                    duckModels[this->nextDuck] = new HMM(3, 9);
                    this->nextDuck = (this->nextDuck + 1) % numDucks;
                    ++ anlz;
                    continue;
                }

                if((!isColoredCurrent && isColored) ||
                   (actModel->getNextGuessProb() > probGuess) || 
                   (numDucks == 1))
                {
                    if(isColored)
                        isColoredCurrent = true;

                    //Do not substitute a colored duck with a white one
                    if(isColored || (!isColoredCurrent && !isColored))
                    {
                        bestObs = actGuess;
                        probGuess = actModel->getNextGuessProb();
                        duckKill = this->nextDuck;
                    }
                }
            }
        }
        this->nextDuck = (this->nextDuck + 1) % numDucks;
        ++ anlz;
    }

    if((duckKill == -1) || ((probGuess < 0.8) && (numDucks != 1)))
    {
//        cout << "Not shooting" << endl;
        return cDontShoot;
    }
   
//    cout << "Taking shot!!" << endl; 
    return *mapIntToObs2(bestObs,
            duckKill, 
            pState.GetDuck(duckKill).GetLastAction().GetMovement());
}

void CPlayer::Guess(vector<CDuck> &pDucks,const CDeadline &pDue)
{
    /*
     * Here you should write your clever algorithms to guess the species of each alive bird.
     * This skeleton guesses that all of them are white... they were the most likely after all!
     */
    for(int i=0;i<pDucks.size();i++)
    {
        if(pDucks[i].IsAlive())
        {            
            ESpecies bestS = SPECIES_BLACK;
            float maxProb = 0;
/*            for(int j = 0; j < 6; ++ j)
            {
                if(speciesModels[j] == NULL)
                    continue;

                float pModel = speciesModels[j]->probabilityOf(obs[i]);
                if(pModel > maxProb)
                {
                    maxProb = pModel;
                    bestS = (ESpecies) j;
                }
            }*/
            
            for(int j = 0; j < pDucks.size(); ++ j)
            {
                //Compare to a dead duck to try to guess color
                if(!pDucks[j].IsAlive() && (pDucks[j].GetSpecies() != SPECIES_UNKNOWN))
                {
                    float probModel = this->duckModels[j]->probabilityOf(this->obs[i]);
                    if((probModel  > blackProbGuess) && (probModel > maxProb))
                    {
                        bestS = pDucks[j].GetSpecies();
                        maxProb = probModel;
                        break;
                    }
                }
            }
            //Could not match to any model, so probably this is the black bird
/*            if(maxProb < blackProb)
                bestS = SPECIES_WHITE;*/

//            cout << "Guessing that duck " << i << " is of species " << bestS << endl;
            pDucks[i].SetSpecies(bestS);
            if(pDue.RemainingUntil() < 100)
                break;
        }
    }
}

void CPlayer::Hit(int pDuck,ESpecies pSpecies)
{
    ++ ducksKilled;

    //Update the model that I have for this species, since this one has more
    //information
    //Killed the black bird, don't be careful about him anymore
    if(pSpecies == 1)
    {
//        cout << "KILLED THE BLACK BIRD!!!" << endl;
        this->blackProb = 0;
        this->blackProbGuess = 0;
    }
/*    if((this->speciesModels[pSpecies] == NULL) && (pSpecies != 1))
        ++ speciesIdentified;*/

    //Only update the model if it hasn't been set to avoid overfitting of the
    //references
    if(this->speciesModels[pSpecies] == NULL)
        this->speciesModels[pSpecies] = duckModels[pDuck];
//    cout << "Killed duck: " << pDuck << " of species: " << pSpecies << endl;
}

int CPlayer::mapObsToInt(CAction obs)
{
    EAction h = obs.GetHAction(), v = obs.GetVAction();
    EMovement dir = obs.GetMovement();
    int res = 0;

    if(dir&BIRD_STOPPED)
        return 24;
    if((h == ACTION_STOP) || (v == ACTION_STOP))
    {
        if(dir&MOVE_EAST)
            if(h == ACTION_ACCELERATE)
            {
                return 16;
            }else if(h == ACTION_KEEPSPEED)
            {
                return 17;
            }

        if(dir&MOVE_WEST)
            if(h == ACTION_ACCELERATE)
            {
                return 18;
            }else if(h == ACTION_KEEPSPEED)
            {
                return 19;
            }

        if(dir&MOVE_DOWN)
            if(v == ACTION_ACCELERATE)
            {
                return 20;
            }else if(v == ACTION_KEEPSPEED)
            {
                return 21;
            }

        if(dir&MOVE_UP)
            if(v == ACTION_ACCELERATE)
            {
                return 22;
            }else if(v == ACTION_KEEPSPEED)
            {
                return 23;
            }
    }else
    {
        if(dir&MOVE_WEST)
            res += 8;

        if(h == ACTION_KEEPSPEED)
            res += 4;

        if(dir&MOVE_DOWN)
            res += 2;

        if(v == ACTION_KEEPSPEED)
            res += 1;
    }
    return res;
}

int CPlayer::mapObsToInt2(CAction obs)
{
    EAction h = obs.GetHAction(), v = obs.GetVAction();
    int ret = 0;

    if(h == ACTION_ACCELERATE)
        ret += 3;

    if(h == ACTION_KEEPSPEED)
        ret += 6;

    if(v == ACTION_ACCELERATE)
        ret += 1;

    if(v == ACTION_KEEPSPEED)
        ret += 2;

    return ret;
}

CAction *CPlayer::mapIntToObs2(int hash, int bird, EMovement last)
{
    EMovement togo;
    if(hash == 0)
    {
        return new CAction(bird, ACTION_STOP, ACTION_STOP, BIRD_STOPPED); 
    }else if(hash == 1)
    {
        if(last&MOVE_UP)
        {
            togo = MOVE_UP;
        }else
        {
            togo = MOVE_DOWN;
        }    
        return new CAction(bird, ACTION_STOP, ACTION_ACCELERATE, togo); 
    }else if(hash == 2)
    {
        if(last&MOVE_UP)
        {
            togo = MOVE_UP;
        }else
        {
            togo = MOVE_DOWN;
        }    
        return new CAction(bird, ACTION_STOP, ACTION_KEEPSPEED, togo); 
    }else if(hash == 3)
    {
        if(last&MOVE_EAST)
        {
            togo = MOVE_EAST;
        }else
        {
            togo = MOVE_WEST;
        }    
        return new CAction(bird, ACTION_ACCELERATE, ACTION_STOP, togo); 
    }else if(hash == 4)
    {
        if(last&MOVE_UP)
        {
            togo = MOVE_UP;
        }else
        {
            togo = MOVE_DOWN;
        }
        if(last&MOVE_EAST)
        {
            togo = (EMovement)(togo|MOVE_EAST);
        }else
        {
            togo = (EMovement)(togo|MOVE_WEST);
        }
        return new CAction(bird, ACTION_ACCELERATE, ACTION_ACCELERATE, togo); 
    }else if(hash == 5)
    {
        if(last&MOVE_UP)
        {
            togo = MOVE_UP;
        }else
        {
            togo = MOVE_DOWN;
        }
        if(last&MOVE_EAST)
        {
            togo = (EMovement)(togo|MOVE_EAST);
        }else
        {
            togo = (EMovement)(togo|MOVE_WEST);
        }
        return new CAction(bird, ACTION_ACCELERATE, ACTION_KEEPSPEED, togo); 
    }else if(hash == 6)
    {
        if(last&MOVE_EAST)
        {
            togo = MOVE_EAST;
        }else
        {
            togo = MOVE_WEST;
        }
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_STOP, togo); 
    }else if(hash == 7)
    {
        if(last&MOVE_UP)
        {
            togo = MOVE_UP;
        }else
        {
            togo = MOVE_DOWN;
        }
        if(last&MOVE_EAST)
        {
            togo = (EMovement)(togo|MOVE_EAST);
        }else
        {
            togo = (EMovement)(togo|MOVE_WEST);
        }
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_ACCELERATE, togo); 
    }else if(hash == 8)
    {
        if(last&MOVE_UP)
        {
            togo = MOVE_UP;
        }else
        {
            togo = MOVE_DOWN;
        }
        if(last&MOVE_EAST)
        {
            togo = (EMovement)(togo|MOVE_EAST);
        }else
        {
            togo = (EMovement)(togo|MOVE_WEST);
        }
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_KEEPSPEED, togo); 
    }
    
    return NULL;
}
CAction *CPlayer::mapIntToObs(int hash, int bird)
{
//    cout << "Hash received: " << hash << endl;
//    cout << "Bird received: " << bird << endl;
    if(hash == 0)
    {
        return new CAction(bird, ACTION_ACCELERATE, ACTION_ACCELERATE, MOVE_EAST|MOVE_UP);
    }else if(hash == 1)
    {
        return new CAction(bird, ACTION_ACCELERATE, ACTION_KEEPSPEED, MOVE_EAST|MOVE_UP);
    }else if(hash == 2)
    {
        return new CAction(bird, ACTION_ACCELERATE, ACTION_ACCELERATE, MOVE_EAST|MOVE_DOWN);
    }else if(hash == 3)
    {
        return new CAction(bird, ACTION_ACCELERATE, ACTION_KEEPSPEED, MOVE_EAST|MOVE_DOWN);
    }else if(hash == 4)
    {
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_ACCELERATE, MOVE_EAST|MOVE_UP);
    }else if(hash == 5)
    {
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_KEEPSPEED, MOVE_EAST|MOVE_UP);
    }else if(hash == 6)
    {
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_ACCELERATE, MOVE_EAST|MOVE_DOWN);
    }else if(hash == 7)
    {
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_KEEPSPEED, MOVE_EAST|MOVE_DOWN);
    }else if(hash == 8)
    {
        return new CAction(bird, ACTION_ACCELERATE, ACTION_ACCELERATE, MOVE_WEST|MOVE_UP);
    }else if(hash == 9)
    {
        return new CAction(bird, ACTION_ACCELERATE, ACTION_KEEPSPEED, MOVE_WEST|MOVE_UP);
    }else if(hash == 10)
    {
        return new CAction(bird, ACTION_ACCELERATE, ACTION_ACCELERATE, MOVE_WEST|MOVE_DOWN);
    }else if(hash == 11)
    {
        return new CAction(bird, ACTION_ACCELERATE, ACTION_KEEPSPEED, MOVE_WEST|MOVE_DOWN);
    }else if(hash == 12)
    {
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_ACCELERATE, MOVE_WEST|MOVE_UP);
    }else if(hash == 13)
    {
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_KEEPSPEED, MOVE_WEST|MOVE_UP);
    }else if(hash == 14)
    {
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_ACCELERATE, MOVE_WEST|MOVE_DOWN);
    }else if(hash == 15)
    {
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_KEEPSPEED, MOVE_WEST|MOVE_DOWN);
    }else if(hash == 16)
    {
        return new CAction(bird, ACTION_ACCELERATE, ACTION_STOP, MOVE_EAST);
    }else  if(hash == 17)
    {
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_STOP, MOVE_EAST);
    }else if(hash == 18)
    {
        return new CAction(bird, ACTION_ACCELERATE, ACTION_STOP, MOVE_WEST);
    }else if(hash == 19)
    {
        return new CAction(bird, ACTION_KEEPSPEED, ACTION_STOP, MOVE_WEST);
    }else if(hash == 20)
    {
        return new CAction(bird, ACTION_STOP, ACTION_ACCELERATE, MOVE_DOWN);
    }else if(hash == 21)
    {
        return new CAction(bird, ACTION_STOP, ACTION_KEEPSPEED, MOVE_DOWN);
    }else if(hash == 22)
    {
        return new CAction(bird, ACTION_STOP, ACTION_ACCELERATE, MOVE_UP);
    }else if(hash == 23)
    {
        return new CAction(bird, ACTION_STOP, ACTION_KEEPSPEED, MOVE_UP);
    }else if(hash == 24)
    {
        return new CAction(bird, ACTION_STOP, ACTION_STOP, BIRD_STOPPED);
    }
}
/*namespace ducks*/ }
